
#include "sock-inet.hpp"

#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdexcept>
#include <unistd.h>

////////////////////////////////////////////////////////////////////////////////

sock_inet::sock_inet(int type, int protocol)
{
    if((sfd = socket(AF_INET, type, protocol)) < 0)
    {
        throw std::runtime_error("socket"); // TODO: specific exception
    }
}

sock_inet::~sock_inet()
{
    close(sfd);
}

void sock_inet::bind(const address& a)
{
    if(::bind(sfd, &a.get_sockaddr(), (int)sizeof(sockaddr)) < 0)
    {
        throw std::runtime_error("bind"); // TODO: specific exception
    }
}

bool sock_inet::wait(const timeout& t)
{
    fd_set rfds;
    timeval tv;

    FD_ZERO(&rfds);
    FD_SET(sfd, &rfds);

    tv.tv_sec = (time_t)(t.get_usec() / 1000000L);
    tv.tv_usec = (suseconds_t)(t.get_usec() % 1000000L);

    int r = select(sfd+1, &rfds, NULL, NULL, &tv);

    if(r < 0)
    {
        throw std::runtime_error("select"); // TODO: specific exception
    }

    return r > 0;
}

////////////////////////////////////////////////////////////////////////////////

sock_inet::address::address(uint32_t host, uint16_t service)
{
    ((sockaddr_in*)&addr)->sin_family = AF_INET;
    ((sockaddr_in*)&addr)->sin_addr.s_addr = htonl(host);
    ((sockaddr_in*)&addr)->sin_port = htons(service);
}

sock_inet::address::operator const sockaddr&() const
{
    return get_sockaddr();
}

const sockaddr& sock_inet::address::get_sockaddr() const
{
    return addr;
}

bool sock_inet::address::operator==(const address& lhs) const
{
    return (*(uint64_t*)&addr) == (*(uint64_t*)&lhs.addr);
}

////////////////////////////////////////////////////////////////////////////////

sock_inet::timeout::timeout(int64_t usec)
    : usec(usec)
{
}

int64_t sock_inet::timeout::get_usec() const
{
    return usec;
}

////////////////////////////////////////////////////////////////////////////////
